package com.aruba.librouter_router.utils

import android.app.Application
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import dalvik.system.DexFile
import kotlinx.coroutines.*
import java.io.IOException
import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.ThreadPoolExecutor

/**
 * 获取所有生成的代码全路径
 * Created by aruba on 2021/11/23.
 */
object ClassUtils {
    /**
     * 获得程序所有的apk(instant run会产生很多split apk)
     */
    private fun getSourcePaths(context: Context): List<String> {
        context.packageManager.getApplicationInfo(
            context.packageName, 0
        ).apply {
            val sourcePaths: MutableList<String> = mutableListOf()
            sourcePaths.add(sourceDir)
            //instant run
            splitSourceDirs?.let {
                sourcePaths.addAll(it)
            }

            return sourcePaths
        }
    }

    /**
     * 根据包名获取路由表
     *
     * @param context
     * @param packageName 包名
     * @return
     * @throws PackageManager.NameNotFoundException
     * @throws IOException
     * @throws InterruptedException
     */
    fun getFileNameByPackageName(context: Application, packageName: String): Set<String> =
        runBlocking {
            val classNames: MutableSet<String> = mutableSetOf()
            val paths = getSourcePaths(context)
            val coroutineScope = CoroutineScope(Dispatchers.IO);
            val jobs = mutableListOf<Job>()
            for (path in paths) {
                coroutineScope.launch {
                    var dexfile: DexFile? = null
                    try {
                        // 加载 apk中的dex 并遍历 获得所有包名为 {packageName} 的类
                        dexfile = DexFile(path)
                        val dexEntries = dexfile.entries()
                        while (dexEntries.hasMoreElements()) {
                            val className = dexEntries.nextElement()
                            if (className.startsWith(packageName)) {
                                classNames.add(className)
                            }
                        }
                    } catch (e: IOException) {
                        e.printStackTrace()
                    } finally {
                        if (null != dexfile) {
                            try {
                                dexfile.close()
                            } catch (e: IOException) {
                                e.printStackTrace()
                            }
                        }
                    }
                }.apply {
                    jobs.add(this)
                }
            }
            jobs.forEach {
                it.join()
            }
            classNames
        }
}